<?php
/**
 * Q_Rule_Set deals with error messages and holds rules. 
 */
class Q_Rule_Set implements Q_Rule_Interface {

    const ALL = '__ALL__';
    protected $_rules = array();
    protected $_msgs = array();
    /**
     * This is used for arrays
     */
    public function validate($data, $fieldsToValidate = null) {
    
        $this->_data = $data;
        if (!is_null($fieldsToValidate)) $specfields = $fieldsToValidate;
        elseif (is_string($fieldsToValidate)) $specfields = array($fieldsToValidate);
        else $specfields = false;
        // loop through each field in data and apply the "ALL" rules to it
        if (isset($this->_rules[self::ALL]) && is_array($this->_rules[self::ALL])) {
            foreach ($this->_data as $field => $value) {
                foreach ($this->_rules[self::ALL] as $rule) {
                    // if this field was not in $fieldsToValidate, don't validate it
                    if ($specfields && !in_array($field, $specfields)) continue;
                    // add the rule to the set for given field
                    $this->add($rule['rule'], $field, $rule['msg']);
                }
            }
        }
        // unset it now so loop doesn't try to use "ALL" rules
        unset($this->_rules[self::ALL]);
        // test each rule and add its messages to the message stack
        // loop through list of fields
        foreach ($this->_rules as $field => $rule) {
            // loop through each rule for each field
            foreach ($rule as $r) {
                // if the rule is a ruleset, it needs special attention (this is bad design - @todo - make set interface the same as rules)
                if ($r['rule'] instanceof Q_Rule_Set) {
                    // populate the ruleset with data and fields to validate
                    if (!$r['rule']->validate($data, $fieldsToValidate)) {
                        // include any messages from set into this set's messages
                        $messages = $r['rule']->getErrors();
                        // loop thtough the messages, adding where necessary
                        foreach ($messages as $field => $msg) {
                            $this->_msgs[$field] = $msg;
                        }
                    }
                } else {
                    // if this field was not in $fieldsToValidate, don't validate it
                    if ($specfields && !in_array($field, $specfields)) continue;
                    $r['rule']->setField($field);
                    if (!$r['rule']->validate($data)) {
                        if (!isset($this->_msgs[$field])) $this->_msgs[$field] = array();
                        // there is an issue here that causes messages to be created multiple times... weird
                        if (!in_array($r['msg'], $this->_msgs[$field])) $this->_msgs[$field][] = $r['msg'];
                    }
                }
            }
        }
        return empty($this->_msgs);
    
    }
    /**
     * Adds a rule / ruleset to the ruleset
     */
    public function add(Q_Rule_Interface $rule, $fields = null, $messages = null) {
    
        // if you don't specify a field, it applies it to all fields
        if (is_null($fields) || empty($fields)) {
            $fields = array(self::ALL);
        }
        // you can provide a string as $fields and it will apply to that field
        if (is_string($fields)) {
            $fields = array($fields);
        }
        // you can provide a string as message and it will be used for all fields
        if (!is_array($messages)) {
            $messages = array($messages);
        }
        // loop through each field and apply its rule
        foreach ($fields as $key => $field) {
            if (!isset($this->_rules[$field])) $this->_rules[$field] = array();
            // if there is a message specifically for this field, use it
            if (isset($messages[$key])) $msg = $messages[$key];
            // otherwise use the last (or possibly only) one in the array
            else $msg = end($messages);
            $this->_rules[$field][] = array('rule' => $rule, 'msg' => $msg);
        }
        return $this;
    
    }
    
    public function getErrors() {
    
        return $this->_msgs;
    
    }

}